/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ourpkr.model.joueur;
import com.sampullara.poker.*;
import com.sampullara.poker.HandRank.Rank;
import java.beans.PropertyChangeSupport;
import java.util.Vector;
import ourpkr.OurPkrApp;
import ourpkr.model.EnsembleCartes;

/**
 *
 * @author anicolas
 */
public class IAta extends Joueur {
    public Rank ranks[];
    public Card.Rank cardsRanks[];
    public IAta.comportement currentComportement;
    public int ancienneFortune;
    public int foldSeuil;
    public boolean playAllIn;
    
    public enum comportement {
        AGGRESSIF , PRUDENT , NORMAL , BLUFF ;
    }
    
    public enum coup {
        PREFLOP , FLOP , TURN , RIVER , OVER ;
    }
    
    public IAta(String nom, int nb)
    {
        pcs = new PropertyChangeSupport(this);
        this.nom = nom;
        this.number = nb;
        ancienneFortune = 0;
        currentComportement = IAta.comportement.NORMAL;
        ranks = Rank.values();
        cardsRanks = Card.Rank.values();
        foldSeuil = 30;
    }
    
    public boolean playPreFlopHand( Cards hand )
    {
        HandRank ourHr = new HandRank( hand );
        
        if( currentComportement == IAta.comportement.BLUFF ||
            isStrongPair(hand)       ||
            isSuitedHand(hand)       ||
            isTwoFiguresHand(hand)   ||
            ( currentComportement != IAta.comportement.PRUDENT && ourHr.getRank() == HandRank.Rank.PAIR) ||
            ( currentComportement != IAta.comportement.PRUDENT   && isStartOfAStraight(hand)) ||
            ( currentComportement == IAta.comportement.AGGRESSIF && hasAFigure(hand) )
          ) 
        {
            return true;
        }
        else 
        {
            return false;
        }
    }
    
    public boolean playPostFlopHand( Cards hand )
    {
        // Statistics
        int cOuts      = calculateOuts();
        Cards ourHand  = getMain().getLibCards();
  
        Cards board    = OurPkrApp.getApplication().getPc().getTour().getTapis().getLibCards();
        
              
        // Building tmpHand
        Cards tmpHand  = getMain().getLibCards();
        for( Card c : board )
        {
            tmpHand.add(c);
        }
        // tmpHand = ourHand + board 
        
        HandRank ourHr = Evaluate.holdem(ourHand, board);
        Cards remaining = getRemainingCards();
        double outPrt  = 100* (double)cOuts / (double)(remaining.size()) ;
        System.out.println("Hand : " + ourHr);
        
        if( currentComportement == IAta.comportement.BLUFF)
        {
            return true;
        }
        else if ( ourHr.getRank() == HandRank.Rank.HIGH && outPrt < foldSeuil)
        {
            // Rien + Outs Faibles : on joue
            return true;
        }
        else if( ourHr.getRank() == HandRank.Rank.PAIR && isStrongPair( tmpHand ))
        {
            // On a une paire forte
            return true;
        }
        else if( ourHr.getRank() != HandRank.Rank.HIGH &&
                 ourHr.getRank() != HandRank.Rank.PAIR
               )
        {
            // Si on a au minimum 2 paires
            if ( currentComportement == IAta.comportement.AGGRESSIF && 
                 ( ourHr.getRank() == HandRank.Rank.STRAIGHTFLUSH) ||
                 ( ourHr.getRank() == HandRank.Rank.FOUROFAKIND  )
               )
            {
                // Si on est aggressif et qu'on a une quinte flush ou un carré => Tapis
                playAllIn = true;
                return true;
            }
            else
            {
                 // Sinon on joue normalement
                 return true;
            }      
        }
      
        return false;
    }
    
    
    
    public boolean isStrongPair( Cards hand )
    {
        HandRank tmpRank = new HandRank( hand );
        
        if( tmpRank.getRank() != HandRank.Rank.PAIR )
        {
            // Not a pair
            return false;
        }
        else
        {
            Card c = hand.get(0); // Getting first hand card
            if( c.getRank() == Card.Rank.ACE  || 
                c.getRank() == Card.Rank.KING || 
                c.getRank() == Card.Rank.QUEEN ||
                c.getRank() == Card.Rank.JACK || 
                c.getRank() == Card.Rank.TEN
              )
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    
    public boolean isSuitedHand( Cards hand )
    {
        if( hand.size() == 2)
        {
            // If regular hand (2 cards)
            if( hand.get(0).getSuit() == hand.get(1).getSuit() )
            {
                // Cards are suited
                return true;
            }
        }
        
        return false;
    }
    
    public boolean hasAFigure( Cards hand )
    {
        if( hand.size() == 2)
        {
            boolean haf = false;
            
            for( Card tmpCard : hand )
            {
                if( tmpCard.getRank() == Card.Rank.ACE   ||
                    tmpCard.getRank() == Card.Rank.KING  ||
                    tmpCard.getRank() == Card.Rank.QUEEN ||
                    tmpCard.getRank() == Card.Rank.JACK  ||
                    tmpCard.getRank() == Card.Rank.TEN 
                  )
                {
                    haf = true;
                }
            }
            
            return haf;
        }
        
        return false;
    }
    
    public boolean isTwoFiguresHand( Cards hand )
    {
        if( hand.size() == 2)
        {
            // If regular hand (2 cards)
            boolean tfh = true;
            
            for( Card tmpCard : hand )
            {
                if( tmpCard.getRank() != Card.Rank.ACE &&
                    tmpCard.getRank() != Card.Rank.KING &&
                    tmpCard.getRank() != Card.Rank.QUEEN &&
                    tmpCard.getRank() != Card.Rank.JACK && 
                    tmpCard.getRank() != Card.Rank.TEN )
                {
                    tfh = false;
                }
            }
            
            return tfh;
        }
        
        return false;
    }
    
    public boolean isStartOfAStraight( Cards hand )
    {
        if( hand.size() == 2)
        {
            // If regular hand (2 cards)
            Card.Rank firstCardRank  = hand.get(0).getRank();
            Card.Rank secondCardRank = hand.get(1).getRank();
            if( firstCardRank != getPreviousRank(secondCardRank) &&
                firstCardRank != getNextRank(secondCardRank)
              )
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        
        return false;
    }
    
    public Card.Rank getPreviousRank( Card.Rank rh )
    {
        int rIndex = 0;
        
        for( int i=0 ; i < cardsRanks.length ; i++ )
        {
            if( cardsRanks[i] == rh ) rIndex = i;
        }
        
        if( rIndex == 0 ) return cardsRanks[ cardsRanks.length - 1 ];
        else return cardsRanks[ rIndex - 1 ];
    }
    
    public Card.Rank getNextRank( Card.Rank rh )
    {
        int rIndex = 0;
        
        for( int i=0 ; i < cardsRanks.length ; i++ )
        {
            if( cardsRanks[i] == rh ) rIndex = i;
        }
        
        if( rIndex == ( cardsRanks.length - 1) ) return cardsRanks[0];
        else return cardsRanks[ rIndex + 1 ];
    }
    
    public Rank[] getHigherRanks( Rank r )
    {
        int rIndex = 0;
       
        // Index de r
        for( int i=0 ; i<ranks.length ; i++)
        {
            if( ranks[i] == r ) rIndex = i; 
        }
        
        Rank outs[] = new Rank[ ranks.length - rIndex - 1];
        // On récupère les rangs plus élevés
        //System.out.println("rIndex ="+rIndex);
        int nbValues = 0;
        for( int i = rIndex+1 ; i < ranks.length ; i++ )
        {
            outs[nbValues] = ranks[i];
            nbValues++;
            // System.out.println("Rang plus haut = "+ranks[i]);
        }
        
        return outs;
    }
    
    public boolean handIsHigher( HandRank h1 , HandRank h2 )
    {
        if ( h1.compareTo(h2) < 0 )
        {
            // h2 better than h1
            return true;
        }
        return false;
    }
   
    public boolean rankIsHigher( Rank r1 , Rank r2 )
    {
        // Is r1 > r2 ?
        int r1Index = 0;
        int r2Index = 0;
        
        // Index de r1
        for( int i=0 ; i<ranks.length ; i++)
        {
            if( ranks[i] == r1 ) r1Index = i; 
        }
        
        // Index de r2
        for( int i=0 ; i<ranks.length ; i++)
        {
            if( ranks[i] == r2 ) r2Index = i; 
        }
        
        return (r1Index>r2Index);
    }
    
    private Cards getRemainingCards()
    {
        EnsembleCartes tmpEns = new EnsembleCartes(true);
        Cards fullDeck = tmpEns.getLibCards();
        Cards ourHand  = getMain().getLibCards();
        Cards board    = OurPkrApp.getApplication().getPc().getTour().getTapis().getLibCards();
        
        // Removing our hand
        for (Card card : ourHand) {
            fullDeck.remove(card);
        }
        
        // Removing board
        for (Card card : board) {
            fullDeck.remove(card);
        }
        
        return fullDeck;  
    }
    
    private int calculateOuts()
    {
        HandRank ourHandHr = new HandRank( getMain().getLibCards() );
        Cards ourHand  = getMain().getLibCards();
        int outs = 0;
        //System.out.println( "Rang notre main = " + ourHandHr.getRank() );
        
        // Rank[] higherRanks = getHigherRanks( ourHandHr.getRank() ); 
        
        Cards remaining = getRemainingCards();
        //System.out.println("Nb remaining cards = " + remaining.size());
        
        for( Card card : remaining )
        {
            Cards board    = OurPkrApp.getApplication().getPc().getTour().getTapis().getLibCards();
            HandRank tmpHr = Evaluate.holdem(ourHand, board);
            board.add(card);
            
            HandRank hr = Evaluate.holdem(ourHand, board);
            if( handIsHigher(tmpHr, hr) )
            {
                outs++;
            }
            
        }
        
       return outs; 
       // System.out.println("Nb outs = "+outs);
       // System.out.println("Proba d'améliorer notre main = "+ ((double)outs/(double)remaining.size())*100 + "%");
    }
  
    public IAta.coup getCurrentCoup()
    {
        String progress = OurPkrApp.getApplication().getPc().getTour().getProgress();
        if( progress == "1er Tour d'encheres") return IAta.coup.PREFLOP ;
        else if( progress == "Flop") return IAta.coup.FLOP;
        else if( progress == "Turn") return IAta.coup.TURN;
        else if( progress == "River") return IAta.coup.RIVER;
        else return IAta.coup.OVER;
        
    }
    
    public boolean isTheRichest(Vector<Joueur> joueurs)
    {
        for( int i=0 ; i < joueurs.size() ; i++ )
        {
            if( joueurs.get(i).getNumber() != this.getNumber() && 
                joueurs.get(i).getFortune() >= this.getFortune()   
              )
            {
                return false;
            }
        }    
        return true;
    }
    
    public synchronized void setComportement()
    {
        Vector<Joueur> joueurs = OurPkrApp.getApplication().getPc().getJoueurs();
        int totalFortune = 0;
        double bluffRand = Math.random() * 20;
        
        for( Joueur j : joueurs )
        {
            totalFortune += j.getFortune();
        }
        
        if( bluffRand < 1 )
        {
            // 5% de chances de bluffer
            this.currentComportement = IAta.comportement.BLUFF;
        }
        else if( getFortune() < ( totalFortune / 10 ) ) 
        {
            // Si on a moins de 10% de la fortune totale, on se calme...
            this.currentComportement = IAta.comportement.PRUDENT;
        }
        else if( getFortune() < (0.8*ancienneFortune) )
        {
            // On a perdu beaucoup d'argent au coup d'avant
            this.currentComportement = IAta.comportement.PRUDENT;
        }
        else if( isTheRichest( joueurs ))
        {
            // On est les plus riches, soyons aggressifs
            this.currentComportement = IAta.comportement.AGGRESSIF;
        }
        else
        {
            // Sinon on joue normalement...
            this.currentComportement = IAta.comportement.NORMAL;
        }   
    }
    
    public void setSeuils()
    {
        if( this.currentComportement == IAta.comportement.AGGRESSIF )     foldSeuil = 60;
        else if ( this.currentComportement == IAta.comportement.NORMAL  ) foldSeuil = 40;
        else if ( this.currentComportement == IAta.comportement.PRUDENT ) foldSeuil = 20;
        else if ( this.currentComportement == IAta.comportement.BLUFF   ) foldSeuil = 100;
    }
    
    public int calculerMise()
    {
        int montant = 0;
        // A ce niveau on a décidé de jouer, mais il faut analyser le pot aussi
        
        if( (fortune + getMiseCourante() - miseMiniTour()) >= 0 )
        {
            // Si on a assez d'argent pour jouer...
            
            Cards ourHand    = getMain().getLibCards();
            Cards board      = OurPkrApp.getApplication().getPc().getTour().getTapis().getLibCards();
            HandRank ourHr   = Evaluate.holdem(ourHand, board);
            int miseCourante = getMiseCourante();
            int miseMinimum  = miseMiniTour();

            
            if( currentComportement == IAta.comportement.BLUFF )
            {
                // Quand on bluff, on ne check pas (but: faire peur aux autres)
                double rndBluff = Math.random() * 3 + 1;
                montant = (int)( rndBluff * miseMinimum );
            
            }
            else if( ourHr.getRank() == HandRank.Rank.HIGH && miseMinimum > 2*miseCourante)
            {
                //la mise minimum est trop importante
                if( getCurrentCoup() == IAta.coup.RIVER || currentComportement == IAta.comportement.AGGRESSIF )
                {
                    // Si on est a la river ca serait bête de se coucher maintenant...
                    montant = 0; // On check
                }
                else
                {
                    montant = -1;
                }
            }
            else if( currentComportement == IAta.comportement.AGGRESSIF )
            {
                if( ourHr.getRank() != HandRank.Rank.HIGH || 
                    ourHr.getRank() != HandRank.Rank.PAIR
                  )
                {
                    // Si mieux qu'une paire, on raise
                    montant = 2 * miseMinimum;
                }
                else
                {
                    montant = 0 ; // Sinon on check
                }
            }
            else if( currentComportement == IAta.comportement.NORMAL )
            {
                if( ourHr.getRank() != HandRank.Rank.HIGH || 
                    ourHr.getRank() != HandRank.Rank.PAIR ||
                    ourHr.getRank() != HandRank.Rank.TWOPAIR
                  )
                {
                    // Si mieux qu'une double paire, on raise
                    montant = 2 * miseMinimum;
                }
                else
                {
                    montant = 0 ; // Sinon on check
                }
            }
            else if( currentComportement == IAta.comportement.PRUDENT )
            {
                if( ourHr.getRank() != HandRank.Rank.HIGH || 
                    ourHr.getRank() != HandRank.Rank.PAIR ||
                    ourHr.getRank() != HandRank.Rank.TWOPAIR ||
                    ourHr.getRank() != HandRank.Rank.THREEOFAKIND
                  )
                {
                    // Si mieux qu'un brelan, on raise
                    montant = 2 * miseMinimum;
                }
                else
                {
                    montant = 0 ; // Sinon on check
                }    
            }
            else
            {
                montant = 0; // Sinon on check
            }
                    
        }
        return montant;
    }
    
    public synchronized int joue(){
        setPlaying(true);
        playAllIn = false;
        Cards ourHand = getMain().getLibCards();
        
        // Get Progress
        IAta.coup currentCoup = getCurrentCoup();
        
        // Setting behavior
        setComportement();
       
        // Set Seuils
        setSeuils();
     
        //System.out.println("------------");
        //System.out.println("Tour courant = " + currentCoup);
        //System.out.println("Comportement courant = " + currentComportement);
        
        if( currentCoup == IAta.coup.PREFLOP )
        {
            ancienneFortune = getFortune();
            
            if( playPreFlopHand(ourHand) )
            {
                double rnd = Math.random() * 3;
                if( rnd < 1 && isStrongPair(ourHand) ) 
                {
                    //System.out.println("Je raise (557) ");
                    setWhatToDo(2*miseMiniTour()); // Raising (Playing)
                }
                else
                {
                    //System.out.println("Je check (562) ");
                    setWhatToDo(0); // Check (Playing)
                }
            }
            else
            {
                //setWhatToDo(0);
                //System.out.println("Je me couche (569)");
                setWhatToDo(-1); // Fold (Not Playing)
            }
        }
        else if( currentCoup == IAta.coup.FLOP || 
                 currentCoup == IAta.coup.TURN ||
                 currentCoup == IAta.coup.RIVER )
        {
            if( playPostFlopHand(ourHand) )
            {
                int mise = calculerMise();
                //System.out.println("Je joue : " + mise + " (580) ");
                if( mise > (getFortune() - miseCourante) ) mise = (getFortune() - miseCourante) ;
                
                setWhatToDo( mise );
            }
            else
            {
                // On se couche
                //System.out.println("Je me couche (586)");
                setWhatToDo(-1);
            }
        }
        else
        {
            // Cas qui doit pas se produire
            //System.out.println("Je me couche (593)");
            setWhatToDo(-1);
        }
        
        setPlaying(false);
        return getWhatToDo();
    }

}
